/***************************************************************************/
/*                                                                         */
/*  psconv.c                                                               */
/*                                                                         */
/*    Some convenience conversions (body).                                 */
/*                                                                         */
/*  Copyright 2006, 2008, 2009 by                                          */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_INTERNAL_POSTSCRIPT_AUX_H

#include "psconv.h"
#include "psauxerr.h"


/* The following array is used by various functions to quickly convert */
/* digits (both decimal and non-decimal) into numbers.                 */

#if 'A' == 65
/* ASCII */

static const FT_Char ft_char_table[128] =
{
    /* 0x00 */
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
};

/* no character >= 0x80 can represent a valid number */
    #define OP >=

#endif /* 'A' == 65 */

#if 'A' == 193
/* EBCDIC */

static const FT_Char ft_char_table[128] =
{
    /* 0x80 */
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
    -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
    -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
    -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
    -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
};

/* no character < 0x80 can represent a valid number */
    #define OP <

#endif /* 'A' == 193 */


  FT_LOCAL_DEF( FT_Int )
  PS_Conv_Strtol( FT_Byte**  cursor,
                  FT_Byte*   limit,
                  FT_Int     base )
  {
    FT_Byte*  p = *cursor;
    FT_Int    num = 0;
    FT_Bool   sign = 0;


    if ( p == limit || base < 2 || base > 36)
return 0;

if (*p == '-' || *p == '+')
{
    sign = FT_BOOL(*p == '-');

    p++;
    if (p == limit)
        return 0;
}

for (; p < limit; p++)
{
    FT_Char c;


    if (IS_PS_SPACE(*p) || *p OP 0x80)
        break;

    c = ft_char_table[*p & 0x7f];

    if (c < 0 || c >= base)
        break;

    num = num * base + c;
}

if (sign)
    num = -num;

*cursor = p;

return num;
}


FT_LOCAL_DEF(FT_Int)
PS_Conv_ToInt(FT_Byte * *cursor,
              FT_Byte * limit)

{
    FT_Byte* p;
    FT_Int num;


    num = PS_Conv_Strtol(cursor, limit, 10);
    p = *cursor;

    if (p < limit && *p == '#')
    {
        *cursor = p + 1;

        return PS_Conv_Strtol(cursor, limit, num);
    }
    else
        return num;
}


FT_LOCAL_DEF(FT_Fixed)
PS_Conv_ToFixed(FT_Byte * *cursor,
                FT_Byte * limit,
                FT_Int power_ten)
{
    FT_Byte* p = *cursor;
    FT_Fixed integral;
    FT_Long decimal = 0, divider = 1;
    FT_Bool sign = 0;


    if (p == limit)
        return 0;

    if (*p == '-' || *p == '+')
    {
        sign = FT_BOOL(*p == '-');

        p++;
        if (p == limit)
            return 0;
    }

    if (*p != '.')
        integral = PS_Conv_ToInt(&p, limit) << 16;
    else
        integral = 0;

    /* read the decimal part */
    if (p < limit && *p == '.')
    {
        p++;

        for (; p < limit; p++)
        {
            FT_Char c;


            if (IS_PS_SPACE(*p) || *p OP 0x80)
                break;

            c = ft_char_table[*p & 0x7f];

            if (c < 0 || c >= 10)
                break;

            if (!integral && power_ten > 0)
            {
                power_ten--;
                decimal = decimal * 10 + c;
            }
            else
            {
                if (divider < 10000000L)
                {
                    decimal = decimal * 10 + c;
                    divider *= 10;
                }
            }
        }
    }

    /* read exponent, if any */
    if (p + 1 < limit && (*p == 'e' || *p == 'E'))
    {
        p++;
        power_ten += PS_Conv_ToInt(&p, limit);
    }

    while (power_ten > 0)
    {
        integral *= 10;
        decimal *= 10;
        power_ten--;
    }

    while (power_ten < 0)
    {
        integral /= 10;
        divider *= 10;
        power_ten++;
    }

    if (decimal)
        integral += FT_DivFix(decimal, divider);

    if (sign)
        integral = -integral;

    *cursor = p;

    return integral;
}


    #if 0
FT_LOCAL_DEF(FT_UInt)
PS_Conv_StringDecode(FT_Byte * *cursor,
                     FT_Byte * limit,
                     FT_Byte * buffer,
                     FT_Offset n)
{
    FT_Byte* p;
    FT_UInt r = 0;


    for (p = *cursor; r < n && p < limit; p++)
    {
        FT_Byte b;


        if (*p != '\\')
        {
            buffer[r++] = *p;

            continue;
        }

        p++;

        switch (*p)
        {
            case 'n':
                b = '\n';
                break;
            case 'r':
                b = '\r';
                break;
            case 't':
                b = '\t';
                break;
            case 'b':
                b = '\b';
                break;
            case 'f':
                b = '\f';
                break;
            case '\r':
                p++;
                if (*p != '\n')
                {
                    b = *p;

                    break;
                }
            /* no break */
            case '\n':
                continue;
                break;
            default:
                if (IS_PS_DIGIT(*p))
                {
                    b = *p - '0';

                    p++;

                    if (IS_PS_DIGIT(*p))
                    {
                        b = b * 8 + *p - '0';

                        p++;

                        if (IS_PS_DIGIT(*p))
                            b = b * 8 + *p - '0';
                        else
                        {
                            buffer[r++] = b;
                            b = *p;
                        }
                    }
                    else
                    {
                        buffer[r++] = b;
                        b = *p;
                    }
                }
                else
                    b = *p;
                break;
        }

        buffer[r++] = b;
    }

    *cursor = p;

    return r;
}
    #endif /* 0 */


FT_LOCAL_DEF(FT_UInt)
PS_Conv_ASCIIHexDecode(FT_Byte * *cursor,
                       FT_Byte * limit,
                       FT_Byte * buffer,
                       FT_Offset n)
{
    FT_Byte* p;
    FT_UInt r = 0;
    FT_UInt w = 0;
    FT_UInt pad = 0x01;


    n *= 2;

    #if 1

    p = *cursor;
    if (n > (FT_UInt)(limit - p))
        n = (FT_UInt)(limit - p);

    /* we try to process two nibbles at a time to be as fast as possible */
    for (; r < n; r++)
    {
        FT_UInt c = p[r];


        if (IS_PS_SPACE(c))
            continue;

        if (c OP 0x80)
            break;

        c = ft_char_table[c & 0x7F];
        if ((unsigned)c >= 16)
            break;

        pad = (pad << 4) | c;
        if (pad & 0x100)
        {
            buffer[w++] = (FT_Byte)pad;
            pad = 0x01;
        }
    }

    if (pad != 0x01)
        buffer[w++] = (FT_Byte)(pad << 4);

    *cursor = p + r;

    return w;

    #else /* 0 */

    for (r = 0; r < n; r++)
    {
        FT_Char c;


        if (IS_PS_SPACE(*p))
            continue;

        if (*p OP 0x80)
            break;

        c = ft_char_table[*p & 0x7f];

        if ((unsigned)c >= 16)
            break;

        if (r & 1)
        {
            *buffer = (FT_Byte)(*buffer + c);
            buffer++;
        }
        else
            *buffer = (FT_Byte)(c << 4);

        r++;
    }

    *cursor = p;

    return (r + 1) / 2;

    #endif /* 0 */

}


FT_LOCAL_DEF(FT_UInt)
PS_Conv_EexecDecode(FT_Byte * *cursor,
                    FT_Byte * limit,
                    FT_Byte * buffer,
                    FT_Offset n,
                    FT_UShort * seed)
{
    FT_Byte* p;
    FT_UInt r;
    FT_UInt s = *seed;


    #if 1

    p = *cursor;
    if (n > (FT_UInt)(limit - p))
        n = (FT_UInt)(limit - p);

    for (r = 0; r < n; r++)
    {
        FT_UInt val = p[r];
        FT_UInt b = (val ^ (s >> 8));


        s = ((val + s) * 52845U + 22719) & 0xFFFFU;
        buffer[r] = (FT_Byte)b;
    }

    *cursor = p + n;
    *seed = (FT_UShort)s;

    #else /* 0 */

    for (r = 0, p = *cursor; r < n && p < limit; r++, p++)
    {
        FT_Byte b = (FT_Byte)(*p ^ (s >> 8));


        s = (FT_UShort)((*p + s) * 52845U + 22719);
        *buffer++ = b;
    }
    *cursor = p;
    *seed = s;

    #endif /* 0 */

    return r;
}


/* END */